package net.wicp.tams.common.grpc.connector;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;

import com.alibaba.fastjson.JSONObject;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;

import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.IOUtil;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.apiext.json.JSONUtil;
import net.wicp.tams.common.connector.ConfigInstance;
import net.wicp.tams.common.connector.beans.CusDynaBean;
import net.wicp.tams.common.connector.executor.busi.ClassLoadManager;
import net.wicp.tams.common.connector.executor.busi.KeyConfigManager;
import net.wicp.tams.common.connector.executor.impl.CommonService;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectException;
import net.wicp.tams.common.exception.param.Request;
import net.wicp.tams.common.exception.param.Response;
import net.wicp.tams.common.grpc.connector.ConnectorServiceGrpc.ConnectorServiceImplBase;
import net.wicp.tams.common.grpc.connector.Out.Builder;
import net.wicp.tams.common.grpc.ribbon.RibbonClient;
import net.wicp.tams.common.http.HttpClient;
import net.wicp.tams.common.http.HttpResult;

@Slf4j
public class ConnectorServer extends ConnectorServiceImplBase {
	private final KeyConfigManager keyConfigManager;
	private final CommonService executor;

	private final static ConnectorServer connectorServer = new ConnectorServer();

	private RibbonClient ribbonClient = null;

	public static ConnectorServer getInst() {
		return connectorServer;
	}

	private ConnectorServer() {
		keyConfigManager = new KeyConfigManager();
		String path = Conf.get("common.grpc.plugin.path");
		path = StringUtil.isNull(path)
				? IOUtil.mergeFolderAndFilePath(IOUtil.getCurFolder(ConnectorServer.class).getParent(), "/plugin")
				: path;
		File pluginDir = new File(path);
		if (!pluginDir.exists()) {
			try {
				FileUtils.forceMkdir(pluginDir);
			} catch (IOException e) {
				log.error("创建插件目录:" + path + "失败", e);
			}
		}
		ClassLoadManager classLoadManager = new ClassLoadManager(path, 1,
				Conf.get("common.grpc.plugin.packagebase").split(","));
		executor = new CommonService();
		executor.setBusiManager(classLoadManager);
		executor.setConfigManager(keyConfigManager);

		if (Boolean.parseBoolean(Conf.get("common.grpc.eureka.enabled"))) {
			ribbonClient = RibbonClient.getInst();
		}
	}

	@Override
	public void call(In request, StreamObserver<Out> responseObserver) {
		ControlInfo controlInfo = request.getControlInfo();
		JSONObject control = new JSONObject();
		control.put(Request.msgId, controlInfo.getMsgId());
		control.put(Request.cache, Boolean.toString(controlInfo.getCache()));
		control.put(Request.requestCommand, controlInfo.getRequestCommand());
		control.put(Request.senderApplication, controlInfo.getSenderApplication());
		control.put(Request.senderChannel, controlInfo.getSenderChannel());
		control.put(Request.senderSystem, controlInfo.getCache());
		JSONObject json = new JSONObject();
		json.put(Request.controlInfo, control);
		JSONObject inputJson = JSONUtil.mergeJSON(json, JSONObject.parseObject(request.getInput()));
		String toApp = controlInfo.getToApplication();
		if (StringUtil.isNull(toApp) || !Boolean.parseBoolean(Conf.get("common.grpc.eureka.enabled"))) {
			CusDynaBean inputBean = keyConfigManager.getInputBeanInputBody(inputJson);
			String appKey = controlInfo.getRequestCommand();
			CusDynaBean outbean = executor.exe(inputBean);
			if (outbean == null) {
				outbean = ConfigInstance.getInstance()
						.getNullOutBean(new ProjectException(ExceptAll.project_nonull, "没有找到" + appKey + "对应的业务实现"));
				log.error("没有找到{}对应的业务实现", appKey);
			}
			responseObserver.onNext(convertToOut(outbean));
		} else {
			if (Boolean.parseBoolean(Conf.get("common.grpc.eureka.enabled")) && ribbonClient == null) {
				CusDynaBean outbean = ConfigInstance.getInstance().getNullOutBean(new ProjectException(
						ExceptAll.project_other, "ribbonClient初始化错误，服务" + toApp + "不支持，此项目可能不支持eureka调用"));
				log.error("ribbonClient初始化错误，服务{}不支持，此项目可能不支持eureka调用。", toApp);
				responseObserver.onNext(convertToOut(outbean));
				responseObserver.onCompleted();
				return;
			}

			// toApplication
			// ribbonClient.addClient("ribbon.properties");// 测试
			ribbonClient.addClientByAppName(toApp);
			DiscoveryEnabledServer serverOne = ribbonClient.getServerOne(toApp);
			if (serverOne == null) {
				CusDynaBean outbean = ConfigInstance.getInstance()
						.getNullOutBean(new ProjectException(ExceptAll.project_nonull, "eureka上没有找到注册的服务" + toApp));
				log.error("eureka上没有找到注册的服务{}", toApp);
				responseObserver.onNext(convertToOut(outbean));
			} else {
				String url = controlInfo.getUrl();
				String urlstr = IOUtil.mergeFolderAndFilePath(serverOne.getInstanceInfo().getHomePageUrl(),
						StringUtil.isNull(url) ? "/connector" : url);
				HttpResult ret = HttpClient.doPost(urlstr, inputJson);
				JSONObject retobj = JSONObject.parseObject(ret.getBodyStr());
				if (retobj.containsKey("error") && retobj.containsKey("timestamp")) {
					// {"timestamp":1517489225341,"status":404,"error":"Not Found","message":"No
					// message available","path":"/connector"}
					responseObserver.onNext(doConvertMicro(retobj));
				} else {
					responseObserver.onNext(doConvert(retobj));
				}
			}
		}
		responseObserver.onCompleted();
	}

	@Override
	public void refresh(Admin request, StreamObserver<Result> responseObserver) {
		String token = Conf.get("common.grpc.admin.token");
		boolean ischeck = false;
		if (StringUtil.isNotNull(token) && !"none".equals(token)) {
			if (token.equals(request.getToken())) {
				ischeck = true;
			} else {
				ischeck = false;
			}
		} else {
			ischeck = true;
		}
		if (ischeck) {
			executor.getConfigManager().refresh();
			executor.getBusiManager().refresh();
			responseObserver.onNext(Result.newBuilder().setRet("1").build());
		} else {
			responseObserver.onNext(Result.newBuilder().setRet("0").build());
		}
		responseObserver.onCompleted();
	}

	private Out convertToOut(CusDynaBean retbean) {
		return doConvert(retbean.getJsonObj());
	}

	@SuppressWarnings("unused")
	private Out convertToOut(String retjsonstr) {
		return doConvert(JSONObject.parseObject(retjsonstr));
	}

	private Out doConvertMicro(JSONObject jsonObj) {
		Builder newBuilder = Out.newBuilder();
		if (jsonObj.containsKey("status")) {
			newBuilder.setHttp(jsonObj.getString("status"));
		}
		if (jsonObj.containsKey("error")) {
			newBuilder.setErrMsg(jsonObj.getString("error"));
		}
		if (jsonObj.containsKey("message")) {
			newBuilder.setErrorDesc(jsonObj.getString("message"));
		}
		if (jsonObj.containsKey("path")) {
			newBuilder.setErrorCode(jsonObj.getString("path"));
		}
		return newBuilder.build();
	}

	@SuppressWarnings("static-access")
	private Out doConvert(JSONObject jsonObj) {
		Builder newBuilder = Out.newBuilder();
		newBuilder.setErrMsg(jsonObj.getString(Response.errMsg));
		jsonObj.remove(Response.errMsg);
		newBuilder.setErrorCode(jsonObj.getString(Response.errorCode));
		jsonObj.remove(Response.errorCode);
		newBuilder.setErrorDesc(jsonObj.getString(Response.errorDesc));
		jsonObj.remove(Response.errorDesc);
		newBuilder.setErrorValue(jsonObj.getIntValue(Response.errorValue));
		jsonObj.remove(Response.errorValue);
		newBuilder.setHttp(jsonObj.getString(Response.http));
		jsonObj.remove(Response.http);
		JSONObject respInfo = jsonObj.getJSONObject(Response.respInfo._self);
		net.wicp.tams.common.grpc.connector.RespInfo.Builder respInfoBuilder = newBuilder.getRespInfoBuilder();
		if (respInfo.containsKey(Response.respInfo.msgId)
				&& StringUtil.isNotNull(respInfo.getString(Response.respInfo.msgId))) {
			respInfoBuilder.setMsgId(respInfo.getString(Response.respInfo.msgId));
		}
		respInfoBuilder.setReceiptApplication(respInfo.getString(Response.respInfo.receiptApplication));
		respInfoBuilder.setMsgIdResp(respInfo.getString(Response.respInfo.msgIdResp));
		respInfoBuilder.setReceiptSystem(respInfo.getString(Response.respInfo.receiptSystem));
		jsonObj.remove(Response.respInfo._self);
		newBuilder.setOutput(JSONObject.toJSONString(jsonObj));
		return newBuilder.build();
	}
}